package com.wrs.project.androidwebrtclib

import android.Manifest
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import androidx.activity.ComponentActivity
import androidx.appcompat.widget.LinearLayoutCompat
import com.wrs.project.androiddemo.MySdpObserver
import com.wrs.project.androiddemo.R
import com.wrs.project.androiddemo.WebSocketClientHelper
import com.wrs.project.module.webrtc.WebRTCHelp
import org.json.JSONException
import org.json.JSONObject
import org.webrtc.AudioTrack
import org.webrtc.Camera2Capturer
import org.webrtc.Camera2Enumerator
import org.webrtc.CameraEnumerator
import org.webrtc.DataChannel
import org.webrtc.DefaultVideoDecoderFactory
import org.webrtc.DefaultVideoEncoderFactory
import org.webrtc.EglBase
import org.webrtc.IceCandidate
import org.webrtc.MediaConstraints
import org.webrtc.MediaStream
import org.webrtc.PeerConnection
import org.webrtc.PeerConnection.IceConnectionState
import org.webrtc.PeerConnection.IceGatheringState
import org.webrtc.PeerConnection.IceServer
import org.webrtc.PeerConnection.SignalingState
import org.webrtc.PeerConnectionFactory
import org.webrtc.RtpReceiver
import org.webrtc.SessionDescription
import org.webrtc.SurfaceTextureHelper
import org.webrtc.SurfaceViewRenderer
import org.webrtc.VideoCapturer
import org.webrtc.VideoDecoderFactory
import org.webrtc.VideoEncoderFactory
import org.webrtc.VideoSource
import org.webrtc.VideoTrack
import java.util.concurrent.ConcurrentHashMap

class WebRTCActivity  : ComponentActivity()  {

    val TAG: String = "MainActivity"

    val AUDIO_TRACK_ID: String = "ARDAMSa0"

    val VIDEO_TRACK_ID: String = "ARDAMSv0"

    val STREAM_IDS: MutableList<String?> = object : ArrayList<String?>() {
        init {
            add("ARDAMS")
        }
    }

    val SURFACE_TEXTURE_HELPER_THREAD_NAME: String = "SurfaceTextureHelperThread"

    val WIDTH: Int = 1280

    val HEIGHT: Int = 720

    val FPS: Int = 30

    private var mEglBase: EglBase? = null
    private var mPeerConnectionFactory: PeerConnectionFactory? = null
    private var mVideoCapturer: VideoCapturer? = null
    private var mAudioTrack: AudioTrack? = null
    private var mVideoTrack: VideoTrack? = null
    private val mWebSocketClientHelper: WebSocketClientHelper = WebSocketClientHelper()

    //    private String mUserId = UUID.randomUUID().toString();
    private  val mUserId = "Android"
    private val mPeerConnectionMap: MutableMap<String?, PeerConnection> =
        ConcurrentHashMap<String?, PeerConnection>()
    private val mRemoteViewMap: MutableMap<String?, SurfaceViewRenderer> =
        ConcurrentHashMap<String?, SurfaceViewRenderer>()


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        WebRTCHelp.getEglBase()
        setContentView(R.layout.activity_multiple_demo)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(
                arrayOf<String>(
                    Manifest.permission.CAMERA,
                    Manifest.permission.RECORD_AUDIO
                ), 100
            )
        }

        (findViewById<View?>(R.id.et_server_url) as EditText).setText("ws://192.168.2.24:8088")
        findViewById<View?>(R.id.btn_connect).setOnClickListener(object : View.OnClickListener {
            override fun onClick(view: View?) {
                val url = (findViewById<View?>(R.id.et_server_url) as EditText).getText().toString()
                    .trim { it <= ' ' }
                mWebSocketClientHelper.connect(url)
            }
        })
        findViewById<View?>(R.id.btn_disconnect).setOnClickListener(object : View.OnClickListener {
            override fun onClick(view: View?) {
                mWebSocketClientHelper.disconnect()
            }
        })
        findViewById<View?>(R.id.btn_join).setOnClickListener(object : View.OnClickListener {
            override fun onClick(view: View?) {
                join()
            }
        })
        findViewById<View?>(R.id.btn_quit).setOnClickListener(object : View.OnClickListener {
            override fun onClick(view: View?) {
                quit()
            }
        })

        mWebSocketClientHelper.setOnWebSocketListener(object : WebSocketClientHelper.OnWebSocketClientListener {
            public override fun onOpen() {
                runOnUiThread(object : Runnable {
                    override fun run() {
                        (findViewById<View?>(R.id.tv_websocket_state) as TextView).setText("WebSocket 已连接")
                    }
                })
            }

            public override fun onClose() {
                runOnUiThread(object : Runnable {
                    override fun run() {
                        (findViewById<View?>(R.id.tv_websocket_state) as TextView).setText("WebSocket 已断开")
                    }
                })
            }

            public override fun onMessage(message: String) {
                try {
                    val jsonObject = JSONObject(message)
                    val msgType = jsonObject.optString("msgType")
                    if (TextUtils.equals("sdp", msgType)) {
                        val type = jsonObject.optString("type")
                        if (TextUtils.equals("offer", type)) {
                            receivedOffer(jsonObject)
                        } else if (TextUtils.equals("answer", type)) {
                            receivedAnswer(jsonObject)
                        }
                    } else if (TextUtils.equals("iceCandidate", msgType)) {
                        receivedCandidate(jsonObject)
                    } else if (TextUtils.equals("otherJoin", msgType) || TextUtils.equals(
                            "join",
                            msgType
                        )
                    ) {
                        receivedOtherJoin(jsonObject)
                    } else if (TextUtils.equals("otherQuit", msgType) || TextUtils.equals(
                            "quit",
                            msgType
                        )
                    ) {
                        receivedOtherQuit(jsonObject)
                    }
                } catch (e: JSONException) {
                    e.printStackTrace()
                }
            }
        })

        // 初始化 PeerConnectionFactory
        initPeerConnectionFactory(this@WebRTCActivity)

        // 创建 EglBase
        mEglBase = EglBase.create()

        // 创建 PeerConnectionFactory
        mPeerConnectionFactory = createPeerConnectionFactory(mEglBase!!)

        // 创建音轨
        mAudioTrack = createAudioTrack(mPeerConnectionFactory!!)

        // 创建视轨
        mVideoCapturer = createVideoCapturer()
        val videoSource = createVideoSource(mPeerConnectionFactory!!, mVideoCapturer!!)
        mVideoTrack = createVideoTrack(mPeerConnectionFactory!!, videoSource)

        // 初始化本地视频渲染控件，这个方法非常重要，不初始化会黑屏
        val svrLocal = findViewById<SurfaceViewRenderer?>(R.id.svr_local)
        svrLocal.init(mEglBase!!.getEglBaseContext(), null)
        mVideoTrack!!.addSink(svrLocal)


        // 开始本地渲染
        // 创建 SurfaceTextureHelper，用来表示 camera 初始化的线程
        val surfaceTextureHelper = SurfaceTextureHelper.create(
            SURFACE_TEXTURE_HELPER_THREAD_NAME,
            mEglBase!!.getEglBaseContext()
        )

        // 初始化视频采集器
        mVideoCapturer!!.initialize(
            surfaceTextureHelper,
            this,
            videoSource.getCapturerObserver()
        )
        mVideoCapturer!!.startCapture(
            WIDTH,
            HEIGHT,
            FPS
        )
    }

    override fun onDestroy() {
        super.onDestroy()
        if (mEglBase != null) {
            mEglBase!!.release()
            mEglBase = null
        }
        if (mVideoCapturer != null) {
            try {
                mVideoCapturer!!.stopCapture()
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
            mVideoCapturer!!.dispose()
            mVideoCapturer = null
        }
        if (mAudioTrack != null) {
            mAudioTrack!!.dispose()
            mAudioTrack = null
        }
        if (mVideoTrack != null) {
            mVideoTrack!!.dispose()
            mVideoTrack = null
        }
        for (peerConnection in mPeerConnectionMap.values) {
            peerConnection.close()
            peerConnection.dispose()
        }
        mPeerConnectionMap.clear()
        val svrLocal = findViewById<SurfaceViewRenderer?>(R.id.svr_local)
        svrLocal.release()
        for (surfaceViewRenderer in mRemoteViewMap.values) {
            surfaceViewRenderer.release()
        }
        mRemoteViewMap.clear()
        mWebSocketClientHelper.disconnect()
    }

    private fun initPeerConnectionFactory(context: Context?) {
        PeerConnectionFactory.initialize(
            PeerConnectionFactory.InitializationOptions.builder(context)
                .createInitializationOptions()
        )
    }

    private fun createPeerConnectionFactory(eglBase: EglBase): PeerConnectionFactory {
        val videoEncoderFactory: VideoEncoderFactory =
            DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), true, true)
        val videoDecoderFactory: VideoDecoderFactory =
            DefaultVideoDecoderFactory(eglBase.getEglBaseContext())
        return PeerConnectionFactory.builder().setVideoEncoderFactory(videoEncoderFactory)
            .setVideoDecoderFactory(videoDecoderFactory).createPeerConnectionFactory()
    }

    private fun createAudioTrack(peerConnectionFactory: PeerConnectionFactory): AudioTrack {
        val audioSource = peerConnectionFactory.createAudioSource(MediaConstraints())
        val audioTrack = peerConnectionFactory.createAudioTrack(
            AUDIO_TRACK_ID,
            audioSource
        )
        audioTrack.setEnabled(true)
        return audioTrack
    }

    private fun createVideoCapturer(): VideoCapturer? {
        var videoCapturer: VideoCapturer? = null
        val cameraEnumerator: CameraEnumerator = Camera2Enumerator(this)
        for (deviceName in cameraEnumerator.getDeviceNames()) {
            // 前摄像头
            if (cameraEnumerator.isFrontFacing(deviceName)) {
                videoCapturer = Camera2Capturer(this, deviceName, null)
            }
        }
        return videoCapturer
    }

    private fun createVideoSource(
        peerConnectionFactory: PeerConnectionFactory,
        videoCapturer: VideoCapturer
    ): VideoSource {
        // 创建视频源
        val videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast())
        return videoSource
    }

    private fun createVideoTrack(
        peerConnectionFactory: PeerConnectionFactory,
        videoSource: VideoSource?
    ): VideoTrack {
        // 创建视轨
        val videoTrack = peerConnectionFactory.createVideoTrack(
            VIDEO_TRACK_ID,
            videoSource
        )
        videoTrack.setEnabled(true)
        return videoTrack
    }

    private fun createPeerConnection(
        peerConnectionFactory: PeerConnectionFactory,
        fromUserId: String?
    ): PeerConnection? {
        // 内部会转成 RTCConfiguration
        val iceServers: MutableList<IceServer?> = java.util.ArrayList<IceServer?>()
        val peerConnection = peerConnectionFactory.createPeerConnection(
            iceServers,
            object : PeerConnection.Observer {
                override fun onSignalingChange(signalingState: SignalingState?) {
                }

                override fun onIceConnectionChange(iceConnectionState: IceConnectionState?) {

                    if (iceConnectionState == IceConnectionState.DISCONNECTED) {
                        val peerConnection = mPeerConnectionMap.get(fromUserId)

                        if (peerConnection != null) {
                            peerConnection.close()
                            mPeerConnectionMap.remove(fromUserId)
                        }
                        runOnUiThread(object : Runnable {
                            override fun run() {
                                val surfaceViewRenderer = mRemoteViewMap.get(fromUserId)
                                if (surfaceViewRenderer != null) {
                                    (surfaceViewRenderer.getParent() as ViewGroup).removeView(
                                        surfaceViewRenderer
                                    )
                                    mRemoteViewMap.remove(fromUserId)
                                }
                            }
                        })
                    }
                }

                override fun onIceConnectionReceivingChange(b: Boolean) {
                }

                override fun onIceGatheringChange(iceGatheringState: IceGatheringState?) {
                }

                override fun onIceCandidate(iceCandidate: IceCandidate) {

                    sendIceCandidate(iceCandidate, fromUserId)
                }

                override fun onIceCandidatesRemoved(iceCandidates: Array<IceCandidate?>?) {
                }

                override fun onAddStream(mediaStream: MediaStream?) {

                    if (mediaStream == null || mediaStream.videoTracks == null || mediaStream.videoTracks.isEmpty()) {
                        return
                    }
                    runOnUiThread(object : Runnable {
                        override fun run() {
                            val surfaceViewRenderer = mRemoteViewMap.get(fromUserId)
                            if (surfaceViewRenderer != null) {
                                mediaStream.videoTracks.get(0).addSink(surfaceViewRenderer)
                            }
                        }
                    })
                }

                override fun onRemoveStream(mediaStream: MediaStream?) {
                }

                override fun onDataChannel(dataChannel: DataChannel?) {
                }

                override fun onRenegotiationNeeded() {
                }

                override fun onAddTrack(
                    rtpReceiver: RtpReceiver?,
                    mediaStreams: Array<MediaStream?>?
                ) {
                }
            })
        return peerConnection
    }

    private fun join() {
        try {
            val jsonObject = JSONObject()
            jsonObject.put("msgType", "join")
            jsonObject.put("userId", mUserId)
            mWebSocketClientHelper.send(jsonObject.toString())
        } catch (e: JSONException) {
            e.printStackTrace()
        }
    }

    private fun quit() {
        try {
            val jsonObject = JSONObject()
            jsonObject.put("msgType", "quit")
            jsonObject.put("userId", mUserId)
            mWebSocketClientHelper.send(jsonObject.toString())
        } catch (e: JSONException) {
            e.printStackTrace()
        }
        Thread(object : Runnable {
            override fun run() {
                for (peerConnection in mPeerConnectionMap.values) {
                    peerConnection.close()
                }
                mPeerConnectionMap.clear()
            }
        }).start()
        for (surfaceViewRenderer in mRemoteViewMap.values) {
            (surfaceViewRenderer.getParent() as ViewGroup).removeView(surfaceViewRenderer)
        }
        mRemoteViewMap.clear()
    }

    private fun sendOffer(offer: SessionDescription, toUserId: String?) {
        try {
            val jsonObject = JSONObject()
            jsonObject.put("msgType", "sdp")
            jsonObject.put("fromUserId", mUserId)
            jsonObject.put("toUserId", toUserId)
            jsonObject.put("type", "offer")
            jsonObject.put("sdp", offer.description)
            mWebSocketClientHelper.send(jsonObject.toString())
        } catch (e: JSONException) {
            e.printStackTrace()
        }
    }

    private fun receivedOffer(jsonObject: JSONObject) {
        val fromUserId = jsonObject.optString("fromUserId")
        var peerConnection = mPeerConnectionMap.get(fromUserId)
        if (peerConnection == null) {
            // 创建 PeerConnection
            peerConnection = createPeerConnection(mPeerConnectionFactory!!, fromUserId)
            // 为 PeerConnection 添加音轨、视轨
            peerConnection!!.addTrack(
                mAudioTrack,
                STREAM_IDS
            )
            peerConnection.addTrack(
                mVideoTrack,
                STREAM_IDS
            )
            mPeerConnectionMap.put(fromUserId, peerConnection)
        }
        runOnUiThread(object : Runnable {
            override fun run() {
                var surfaceViewRenderer = mRemoteViewMap.get(fromUserId)
                if (surfaceViewRenderer == null) {
                    // 初始化 SurfaceViewRender ，这个方法非常重要，不初始化黑屏
                    surfaceViewRenderer = SurfaceViewRenderer(this@WebRTCActivity)
                    surfaceViewRenderer.init(mEglBase!!.getEglBaseContext(), null)
                    surfaceViewRenderer.setLayoutParams(
                        LinearLayout.LayoutParams(
                            dp2px(
                                this@WebRTCActivity,
                                90f
                            ), dp2px(this@WebRTCActivity, 160f)
                        )
                    )
                    val llRemotes = findViewById<LinearLayoutCompat?>(R.id.ll_remotes)
                    llRemotes.addView(surfaceViewRenderer)
                    mRemoteViewMap.put(fromUserId, surfaceViewRenderer)
                }
            }
        })
        val type = jsonObject.optString("type")
        val sdp = jsonObject.optString("sdp")
        val finalPeerConnection = peerConnection
        // 将 offer sdp 作为参数 setRemoteDescription
        val sessionDescription =
            SessionDescription(SessionDescription.Type.fromCanonicalForm(type), sdp)
        peerConnection.setRemoteDescription(object : MySdpObserver {
            public override fun onCreateSuccess(sessionDescription: SessionDescription?) {
            }

            public override fun onSetSuccess() {

                // 通过 PeerConnection 创建 answer，获取 sdp
                val mediaConstraints = MediaConstraints()
                finalPeerConnection.createAnswer(object : MySdpObserver {
                    public override fun onCreateSuccess(sessionDescription: SessionDescription) {

                        // 将 answer sdp 作为参数 setLocalDescription
                        finalPeerConnection.setLocalDescription(object : MySdpObserver {
                            public override fun onCreateSuccess(sessionDescription: SessionDescription?) {
                            }

                            public override fun onSetSuccess() {

                                // 发送 answer sdp
                                sendAnswer(sessionDescription, fromUserId)
                            }
                        }, sessionDescription)
                    }

                    public override fun onSetSuccess() {
                    }
                }, mediaConstraints)
            }
        }, sessionDescription)
    }

    private fun sendAnswer(answer: SessionDescription, toUserId: String?) {
        try {
            val jsonObject = JSONObject()
            jsonObject.put("msgType", "sdp")
            jsonObject.put("fromUserId", mUserId)
            jsonObject.put("toUserId", toUserId)
            jsonObject.put("type", "answer")
            jsonObject.put("sdp", answer.description)
            mWebSocketClientHelper.send(jsonObject.toString())
        } catch (e: JSONException) {
            e.printStackTrace()
        }
    }

    private fun receivedAnswer(jsonObject: JSONObject) {
        val fromUserId = jsonObject.optString("fromUserId")
        var peerConnection = mPeerConnectionMap.get(fromUserId)
        if (peerConnection == null) {
            peerConnection = createPeerConnection(mPeerConnectionFactory!!, fromUserId)
            peerConnection!!.addTrack(
                mAudioTrack,
                STREAM_IDS
            )
            peerConnection.addTrack(
                mVideoTrack,
                STREAM_IDS
            )
            mPeerConnectionMap.put(fromUserId, peerConnection)
        }
        runOnUiThread(object : Runnable {
            override fun run() {
                var surfaceViewRenderer = mRemoteViewMap.get(fromUserId)
                if (surfaceViewRenderer == null) {
                    // 初始化 SurfaceViewRender ，这个方法非常重要，不初始化黑屏
                    surfaceViewRenderer = SurfaceViewRenderer(this@WebRTCActivity)
                    surfaceViewRenderer.init(mEglBase!!.getEglBaseContext(), null)
                    surfaceViewRenderer.setLayoutParams(
                        LinearLayout.LayoutParams(
                            dp2px(
                                this@WebRTCActivity,
                                90f
                            ), dp2px(this@WebRTCActivity, 160f)
                        )
                    )
                    val llRemotes = findViewById<LinearLayoutCompat?>(R.id.ll_remotes)
                    llRemotes.addView(surfaceViewRenderer)
                    mRemoteViewMap.put(fromUserId, surfaceViewRenderer)
                }
            }
        })
        val type = jsonObject.optString("type")
        val sdp = jsonObject.optString("sdp")
        // 收到 answer sdp，将 answer sdp 作为参数 setRemoteDescription
        val sessionDescription =
            SessionDescription(SessionDescription.Type.fromCanonicalForm(type), sdp)
        peerConnection.setRemoteDescription(object : MySdpObserver {
            public override fun onCreateSuccess(sessionDescription: SessionDescription?) {
            }

            public override fun onSetSuccess() {

            }
        }, sessionDescription)
    }

    private fun sendIceCandidate(iceCandidate: IceCandidate, toUserId: String?) {
        try {
            val jsonObject = JSONObject()
            jsonObject.put("msgType", "iceCandidate")
            jsonObject.put("fromUserId", mUserId)
            jsonObject.put("toUserId", toUserId)
            jsonObject.put("id", iceCandidate.sdpMid)
            jsonObject.put("label", iceCandidate.sdpMLineIndex)
            jsonObject.put("candidate", iceCandidate.sdp)
            mWebSocketClientHelper.send(jsonObject.toString())
        } catch (e: JSONException) {
            e.printStackTrace()
        }
    }

    private fun receivedCandidate(jsonObject: JSONObject) {
        val fromUserId = jsonObject.optString("fromUserId")
        val peerConnection = mPeerConnectionMap.get(fromUserId)
        if (peerConnection == null) {
            return
        }
        val id = jsonObject.optString("id")
        val label = jsonObject.optInt("label")
        val candidate = jsonObject.optString("candidate")
        val iceCandidate = IceCandidate(id, label, candidate)
        peerConnection.addIceCandidate(iceCandidate)
    }

    @Throws(JSONException::class)
    private fun receivedOtherJoin(jsonObject: JSONObject) {
        val userId = jsonObject.optString("userId")
        var peerConnection = mPeerConnectionMap.get(userId)
        if (peerConnection == null) {
            // 创建 PeerConnection
            peerConnection = createPeerConnection(mPeerConnectionFactory!!, userId)
            // 为 PeerConnection 添加音轨、视轨
            peerConnection!!.addTrack(
                mAudioTrack,
                STREAM_IDS
            )
            peerConnection.addTrack(
                mVideoTrack,
                STREAM_IDS
            )
            mPeerConnectionMap.put(userId, peerConnection)
        }
        runOnUiThread(object : Runnable {
            override fun run() {
                var surfaceViewRenderer = mRemoteViewMap.get(userId)
                if (surfaceViewRenderer == null) {
                    // 初始化 SurfaceViewRender ，这个方法非常重要，不初始化黑屏
                    surfaceViewRenderer = SurfaceViewRenderer(this@WebRTCActivity)
                    surfaceViewRenderer.init(mEglBase!!.getEglBaseContext(), null)
                    surfaceViewRenderer.setLayoutParams(
                        LinearLayout.LayoutParams(
                            dp2px(
                                this@WebRTCActivity,
                                90f
                            ), dp2px(this@WebRTCActivity, 160f)
                        )
                    )
                    val llRemotes = findViewById<LinearLayoutCompat?>(R.id.ll_remotes)
                    llRemotes.addView(surfaceViewRenderer)
                    mRemoteViewMap.put(userId, surfaceViewRenderer)
                }
            }
        })
        val finalPeerConnection = peerConnection
        // 通过 PeerConnection 创建 offer，获取 sdp

        val mediaConstraints = MediaConstraints()
        peerConnection.createOffer(object : MySdpObserver {
            public override fun onCreateSuccess(sessionDescription: SessionDescription) {

                // 将 offer sdp 作为参数 setLocalDescription
                finalPeerConnection.setLocalDescription(object : MySdpObserver {
                    public override fun onCreateSuccess(sessionDescription: SessionDescription?) {
                    }

                    public override fun onSetSuccess() {

                        // 发送 offer sdp
                        sendOffer(sessionDescription, userId)
                    }
                }, sessionDescription)
            }

            public override fun onSetSuccess() {
            }
        }, mediaConstraints)
    }

    @Throws(JSONException::class)
    private fun receivedOtherQuit(jsonObject: JSONObject) {
        val userId = jsonObject.optString("userId")
        val peerConnection = mPeerConnectionMap.get(userId)
        if (peerConnection != null) {
            peerConnection.close()
            mPeerConnectionMap.remove(userId)
        }
        runOnUiThread(object : Runnable {
            override fun run() {
                val surfaceViewRenderer = mRemoteViewMap.get(userId)
                if (surfaceViewRenderer != null) {
                    (surfaceViewRenderer.getParent() as ViewGroup).removeView(surfaceViewRenderer)
                    mRemoteViewMap.remove(userId)
                }
            }
        })
    }

    fun dp2px(context: Context, dp: Float): Int {
        val density = context.getResources().getDisplayMetrics().density
        return (dp * density + 0.5f).toInt()
    }
}